package main

import (
	"context"
	"fmt"
	//"io"
	"log"
	//"os/exec"
	//"strings"
	//"bufio"

	//"net/http"
	//_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"
	"time"

	"go.etcd.io/etcd/clientv3"
)

var NodeIP string
var Cmds string
var VipUp string
var VipDown string

func init() {
	NodeIP, _ = os.Hostname()
	NetCard := Conf.NetCard
	VIP := Conf.VIP
	NUM := Conf.NUM
	Cmds = Conf.Cmds
	//根据是否填写VIP来编写VIP上下线信息
	//TODO arping是否需要?
	if len(VIP) > 0 {
		VipUp = fmt.Sprintf("ip addr add %s/32 dev %s label %s:%s", VIP, NetCard, NetCard, NUM)
		VipDown = fmt.Sprintf("ip addr del %s/32 dev %s", VIP, NetCard)
	}
}

type CmdConfig struct {
	EtcdEndpoints []string
	VIP           string
	Cmds          string //需要启动的进程, 这里是prometheus服务进程
	NetCard       string //VIP映射的IP
	NUM           string //VIP的NUM使用
	Lock          string
}

var (
	lease                  clientv3.Lease
	ctx                    context.Context
	cancelFunc             context.CancelFunc
	leaseId                clientv3.LeaseID
	leaseGrantResponse     *clientv3.LeaseGrantResponse
	leaseKeepAliveChan     <-chan *clientv3.LeaseKeepAliveResponse
	leaseKeepAliveResponse *clientv3.LeaseKeepAliveResponse
	txn                    clientv3.Txn
	txnResponse            *clientv3.TxnResponse
	kv                     clientv3.KV
)

type ETCD struct {
	client *clientv3.Client
	cfg    clientv3.Config
	err    error
}

//连接etcd
func New(endpoints ...string) (*ETCD, error) {
	cfg := clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: time.Second * 5,
	}

	client, err := clientv3.New(cfg)
	if err != nil {
		log.Println("连接ETCD失败")
		return nil, err
	}

	etcd := &ETCD{
		cfg:    cfg,
		client: client,
	}

	log.Println("连接ETCD成功")
	return etcd, nil
}

// ip 为本机IP
func (etcd *ETCD) Newleases_lock(ip string, cmd string, job chan *Cmd) error {
	lock := Conf.Lock
	lease := clientv3.NewLease(etcd.client)
	defer lease.Close()
	leaseGrantResponse, err := lease.Grant(context.TODO(), 5)
	if err != nil {
		log.Println(err)
		return err
	}
	leaseId := leaseGrantResponse.ID
	ctx, cancelFunc := context.WithCancel(context.TODO())
	defer cancelFunc()
	defer lease.Revoke(context.TODO(), leaseId)
	leaseKeepAliveChan, err := lease.KeepAlive(ctx, leaseId)
	if err != nil {
		log.Println(err)
		return err
	}

	kv := clientv3.NewKV(etcd.client)
	txn := kv.Txn(context.TODO())
	txn.If(clientv3.Compare(clientv3.CreateRevision(lock), "=", 0)).Then(
		clientv3.OpPut(lock, ip, clientv3.WithLease(leaseId))).Else(
		clientv3.OpGet(lock))
	txnResponse, err := txn.Commit()
	if err != nil {
		log.Println(err)
		return err
	}
	//抢到锁
	if txnResponse.Succeeded {
		log.Println("抢到锁了")
		log.Printf(" %s 成为leader节点", ip)
		// select {
		// case leaseKeepAliveResponse = <-leaseKeepAliveChan:
		// default:

		// }
		go func() {
			for {
				select {
				case leaseKeepAliveResponse = <-leaseKeepAliveChan:
					if leaseKeepAliveResponse != nil {
						log.Println("续租成功,leaseID :", leaseKeepAliveResponse.ID)
					} else {
						log.Println("续租失败")
						return
					}

				}
			}
		}()
		if len(VipUp) > 0 {
			log.Println("上线VIP")
			vipup := NewCommand(VipUp)
			vipup.Run()
		}
		defer func() {
			if len(VipDown) > 0 {
				log.Println("下线VIP")
				vipdown := NewCommand(VipDown)
				vipdown.Run()
			}
		}()
		c := NewCommand(cmd)
        c.Start()
	
		// outfile, err := os.Create("./out.txt")
		// if err != nil {
		// 	panic(err)
		// }
		// defer outfile.Close()
		// outfile.WriteString(c.Status.Stderr)
		job <- c
		c.Wait()
		//c.Run()
		status := c.Status
		log.Println("status.Error: ", status.Error)
		log.Println("status.PID: ", status.PID)
		log.Println("服务停止")
		time.Sleep(time.Second * 2)
		c.Stop()
		<- job
	} else {
		log.Println("没抢到锁", string(txnResponse.Responses[0].GetResponseRange().Kvs[0].Value))
		log.Println("继续抢")
		time.Sleep(time.Second * 1)
	}
	log.Println("退出Newleases_lock函数")
	return nil
}

func main() {
	// 开启pprof
	// go func() {
	// 	ip := "0.0.0.0:6060"
	// 	if err := http.ListenAndServe(ip, nil); err != nil {
	// 		fmt.Printf("start pprof failed on %s\n", ip)
	// 		os.Exit(1)
	// 	}
	// }()
	
	var job  = make (chan *Cmd, 1)


	etcd, err := New(Conf.EtcdEndpoints...)
	if err != nil {
		log.Println(err)
		return
	}

	defer etcd.client.Close()

	go func() {
		for {
			//抢lock,抢到为主,启动进程,挂载vip
			etcd.Newleases_lock(NodeIP, Cmds, job)
			//说明锁释放了
			//TODO 下线 VIP? 这里要不要回收, 需要回收

		}
	}()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	<-signalChan
	// 下面进行资源回收，清理工作
	j := <- job
	j.Stop()
	//TODO回收VIP
	if len(VipDown) > 0 {
		log.Println("下线VIP")
		vipdown := NewCommand(VipDown)
		vipdown.Run()
	}
	
	log.Println("退出...")
}
